//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioModels
{
    using Microsoft.Research.DataLayer;
    using System.Text;
    using System;
    using System.ComponentModel;

    /// <summary>
    /// The paramter model.
    /// </summary>
    public class ParameterModel : IDataErrorInfo
    {
        /// <summary>
        /// Gets or sets the name of the parameter.
        /// </summary>
        /// <value>The name of the parameter.</value>
        public string ParameterName { get; set; }
        
        /// <summary>
        /// Gets or sets the parameter label.
        /// </summary>
        /// <value>The parameter label.</value>
        public string ParameterLabel { get; set; }
       
        /// <summary>
        /// Gets or sets the type of the data.
        /// </summary>
        /// <value>The type of the data.</value>
        public string DataType { get; set; }
        
        /// <summary>
        /// Gets or sets the param direction.
        /// </summary>
        /// <value>The param direction.</value>
        public DirectionType ParameterDirection { get; set; }
        
        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ParameterModel"/> is required.
        /// </summary>
        /// <value><c>true</c> if required; otherwise, <c>false</c>.</value>
        public bool Required { get; set; }
        
        /// <summary>
        /// Gets or sets the default value.
        /// </summary>
        /// <value>The default value.</value>
        public string DefaultValue { get; set; }
        
        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>The id.</value>
        public Guid Id { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether contains invalid value.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if contains invalid value; otherwise, <c>false</c>.
        /// </value>
        public bool ContainsInvalidValue { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParameterModel"/> class.
        /// </summary>
        /// <param name="activityParameter">The activity parameter.</param>
        public ParameterModel(ActivityParameter activityParameter)
        {
            this.ParameterName = activityParameter.Name;
            this.ParameterLabel = string.IsNullOrEmpty(activityParameter.Label) ? activityParameter.Name : activityParameter.Label;
            this.DataType = activityParameter.Type;
            this.ParameterDirection = activityParameter.Direction;
            this.Required = activityParameter.Required;
            this.DefaultValue = activityParameter.DefaultValue;
            this.Id = ((IObject)activityParameter).ID;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ParameterModel"/> class.
        /// </summary>
        /// <param name="paramModel">The param model.</param>
        public ParameterModel(ParameterModel parameterModel)
        {
            this.ParameterName = parameterModel.ParameterName;
            this.ParameterLabel = parameterModel.ParameterLabel;
            this.DataType = parameterModel.DataType;
            this.ParameterDirection = parameterModel.ParameterDirection;
            this.Required = parameterModel.Required;
            this.DefaultValue = parameterModel.DefaultValue;
            this.Id = parameterModel.Id;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            StringBuilder builder = new StringBuilder(this.ParameterName);
            builder.Append("_");
            builder.Append(this.DataType);
            return builder.ToString();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
        public override bool Equals(object obj)
        {
            bool equal = false;
            ParameterModel paramModel = obj as ParameterModel;
            if (paramModel != null
                && ParameterModel.CheckEquality(paramModel.DefaultValue, this.DefaultValue)
                && paramModel.Required == this.Required
                && paramModel.Id.Equals(this.Id))
            {
                equal = true;
            }

            return equal;
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Updates the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        public void Update(ParameterModel parameterModel)
        {
            this.ParameterName = parameterModel.ParameterName;
            this.ParameterLabel = parameterModel.ParameterLabel;
            this.DataType = parameterModel.DataType;
            this.ParameterDirection = parameterModel.ParameterDirection;
            this.Required = parameterModel.Required;
            this.DefaultValue = parameterModel.DefaultValue;
            this.Id = parameterModel.Id;
        }

        /// <summary>
        /// Checks the equality.
        /// </summary>
        /// <param name="str1">The STR1.</param>
        /// <param name="str2">The STR2.</param>
        /// <returns></returns>
        private static bool CheckEquality(string str1, string str2)
        {
            bool equal = false;
            if ((string.IsNullOrEmpty(str1) && string.IsNullOrEmpty(str2)) ||
                (!string.IsNullOrEmpty(str1) && !string.IsNullOrEmpty(str2) && str1.Equals(str2)))
            {
                equal = true;
            }

            return equal;
        }
        
        /// <summary>
        /// Checks the data validity.
        /// </summary>
        /// <returns>
        /// True for valid data, and false otherwise.
        /// </returns>
        private bool CheckDataValidity()
        {
            bool validData = true;
            if (!string.IsNullOrEmpty(this.DefaultValue))
            {
                object parsedValue = null;
                Type dataType = Type.GetType(this.DataType);

                // If the type is one of the buitin(in GAC) datatypes then convert it.
                if (dataType != null)
                {
                    try
                    {
                        parsedValue = TypeDescriptor.GetConverter(dataType).ConvertFromString(this.DefaultValue);
                        if (null == parsedValue)
                        {
                            validData = false;
                        }
                    }
                    catch (ArgumentException) { validData = false; }
                    catch (NotSupportedException) { validData = false; }
                    catch (Exception) { validData = false; }
                }
            }

            return validData;
        }

        #region IDataErrorInfo Members

        /// <summary>
        /// Gets an error message indicating what is wrong with this object.
        /// </summary>
        /// <value></value>
        /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
        public string Error
        {
            get { return "Invalid data has been entered"; }
        }

        /// <summary>
        /// Gets the <see cref="System.String"/> with the specified column name.
        /// </summary>
        /// <value></value>
        public string this[string columnName]
        {
            get
            {
                string result = null;
                if (columnName == "DefaultValue")
                {
                    this.ContainsInvalidValue = !this.CheckDataValidity();
                    if (this.ContainsInvalidValue)
                    {
                        result = "Invalid data entered.";
                    }
                }

                return result;
            }
        }

        #endregion
    }
}
